<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="chrome=1">

    

    <title>
      JavaScript响应式原理 | 江月夜 
    </title>

    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    
      <meta name="author" content="江月夜">
    
    

    <meta name="description" content="前言前端的很多js框架，比如Vue，React，Angular都有自己的响应式引擎。了解其响应原理，有助于提升自己并更加有效的工作。这篇文章模拟Vue的响应原理，我们一起来实现这个有趣的事情。 响应式编程响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流，而相关的计算">
<meta name="keywords" content="前端">
<meta property="og:type" content="article">
<meta property="og:title" content="JavaScript响应式原理 | 江月夜">
<meta property="og:url" content="http://yoursite.com/2018/10/17/reactive/index.html">
<meta property="og:site_name" content="江月夜">
<meta property="og:description" content="前言前端的很多js框架，比如Vue，React，Angular都有自己的响应式引擎。了解其响应原理，有助于提升自己并更加有效的工作。这篇文章模拟Vue的响应原理，我们一起来实现这个有趣的事情。 响应式编程响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流，而相关的计算模型会自动将变化的值通过数据流进行传播。例如，在命令式编程环境中，a=b+c表示">
<meta property="og:locale" content="zh-CN">
<meta property="og:image" content="http://yoursite.com/images/reactive-log.png">
<meta property="og:updated_time" content="2018-10-29T09:07:59.737Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="JavaScript响应式原理 | 江月夜">
<meta name="twitter:description" content="前言前端的很多js框架，比如Vue，React，Angular都有自己的响应式引擎。了解其响应原理，有助于提升自己并更加有效的工作。这篇文章模拟Vue的响应原理，我们一起来实现这个有趣的事情。 响应式编程响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流，而相关的计算模型会自动将变化的值通过数据流进行传播。例如，在命令式编程环境中，a=b+c表示">
<meta name="twitter:image" content="http://yoursite.com/images/reactive-log.png">
    
    
    
      <link rel="icon" type="image/x-icon" href="/favicon.png">
    
    <link rel="stylesheet" href="/css/uno.css">
    <link rel="stylesheet" href="/css/highlight.css">
    <link rel="stylesheet" href="/css/archive.css">
    <link rel="stylesheet" href="/css/china-social-icon.css">

</head>
<body>

    <span class="mobile btn-mobile-menu">
        <i class="icon icon-list btn-mobile-menu__icon"></i>
        <i class="icon icon-x-circle btn-mobile-close__icon hidden"></i>
    </span>

    

<header class="panel-cover panel-cover--collapsed">


  <div class="panel-main">

  
    <div class="panel-main__inner panel-inverted">
    <div class="panel-main__content">

        

        <h1 class="panel-cover__title panel-title"><a href="/" title="link to homepage">江月夜</a></h1>
        <hr class="panel-cover__divider" />

        
        <p class="panel-cover__description">
          腹有诗书气自华
        </p>
        <hr class="panel-cover__divider panel-cover__divider--secondary" />
        

        <div class="navigation-wrapper">

          <nav class="cover-navigation cover-navigation--primary">
            <ul class="navigation">

              
                
                <li class="navigation__item"><a href="/#blog" title="" class="blog-button">首页</a></li>
              
                
                <li class="navigation__item"><a href="/about" title="" class="">关于</a></li>
              
                
                <li class="navigation__item"><a href="/archive" title="" class="">归档</a></li>
              

            </ul>
          </nav>

          <!-- ----------------------------
To add a new social icon simply duplicate one of the list items from below
and change the class in the <i> tag to match the desired social network
and then add your link to the <a>. Here is a full list of social network
classes that you can use:

    icon-social-500px
    icon-social-behance
    icon-social-delicious
    icon-social-designer-news
    icon-social-deviant-art
    icon-social-digg
    icon-social-dribbble
    icon-social-facebook
    icon-social-flickr
    icon-social-forrst
    icon-social-foursquare
    icon-social-github
    icon-social-google-plus
    icon-social-hi5
    icon-social-instagram
    icon-social-lastfm
    icon-social-linkedin
    icon-social-medium
    icon-social-myspace
    icon-social-path
    icon-social-pinterest
    icon-social-rdio
    icon-social-reddit
    icon-social-skype
    icon-social-spotify
    icon-social-stack-overflow
    icon-social-steam
    icon-social-stumbleupon
    icon-social-treehouse
    icon-social-tumblr
    icon-social-twitter
    icon-social-vimeo
    icon-social-xbox
    icon-social-yelp
    icon-social-youtube
    icon-social-zerply
    icon-mail

-------------------------------->

<!-- add social info here -->



<nav class="cover-navigation navigation--social">
  <ul class="navigation">

    
      <!-- Github -->
      <li class="navigation__item">
        <a href="https://github.com/ajiu9" title="">
          <i class='icon icon-social-github'></i>
          <span class="label">GitHub</span>
        </a>
      </li>
    

    <!-- China social icon -->
    <!--
    
      <li class="navigation__item">
        <a href="" title="">
          <i class='icon cs-icon-douban'></i>
          <span class="label">Douban</span>
        </a>
      </li>

      <li class="navigation__item">
        <a href="" title="">
          <i class='icon cs-icon-weibo'></i>
          <span class="label">Weibo</span>
        </a>
      </li>

    -->



  </ul>
</nav>



        </div>

      </div>

    </div>

    <div class="panel-cover--overlay"></div>
  </div>
</header>

    <div class="content-wrapper">
        <div class="content-wrapper__inner entry">
            

<article class="post-container post-container--single">

  <header class="post-header">
    
    <h1 class="post-title">JavaScript响应式原理</h1>

    

    <div class="post-meta">
      <time datetime="2018-10-17" class="post-meta__date date">2018-10-17</time> 

      <span class="post-meta__tags tags">

          

          
             &#8226; 标签:
            <font class="tags">
              <a class="tags-link" href="/tags/前端/">前端</a>
            </font>
          

      </span>
    </div>
    
    

  </header>

  <section id="post-content" class="article-content post">
    <h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>前端的很多js框架，比如<code>Vue，React，Angular</code>都有自己的响应式引擎。了解其响应原理，有助于提升自己并更加有效的工作。这篇文章模拟Vue的响应原理，我们一起来实现这个有趣的事情。</p>
<h4 id="响应式编程"><a href="#响应式编程" class="headerlink" title="响应式编程"></a>响应式编程</h4><p>响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流，而相关的计算模型会自动将变化的值通过数据流进行传播。<br>例如，在命令式编程环境中，<code>a=b+c</code>表示将表达式的结果赋给a，而之后改变b或c的值不会影响a。但在响应式编程中，a的值会随着b或c的更新而更新。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = <span class="number">1</span></span><br><span class="line"><span class="keyword">let</span> b = <span class="number">2</span></span><br><span class="line"><span class="keyword">let</span> c = a + b <span class="comment">// 3</span></span><br><span class="line">b = <span class="number">5</span></span><br><span class="line"><span class="built_in">console</span>.log(c) <span class="comment">// 3</span></span><br><span class="line"><span class="comment">// 这是命令是编程，b的改变不会改变c的值，响应式就是希望b改变后c能相应的改变</span></span><br></pre></td></tr></table></figure></p>
<h4 id="Vue中的响应式"><a href="#Vue中的响应式" class="headerlink" title="Vue中的响应式"></a>Vue中的响应式</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">&lt;div id=<span class="string">"app"</span>&gt;</span><br><span class="line">  &lt;input id=<span class="string">"price"</span> :value=price /&gt;</span><br><span class="line">  &lt;input id=<span class="string">"quantity"</span> :value=quantity /&gt;</span><br><span class="line">  &lt;span&gt;&#123;&#123;total&#125;&#125;&lt;<span class="regexp">/span&gt;</span></span><br><span class="line"><span class="regexp">&lt;/</span>div&gt;</span><br><span class="line">&lt;script&gt;</span><br><span class="line">  <span class="keyword">var</span> vm = <span class="keyword">new</span> Vue(&#123;</span><br><span class="line">    el: <span class="string">"#app"</span>,</span><br><span class="line">    data() &#123;</span><br><span class="line">      <span class="keyword">return</span> &#123;</span><br><span class="line">        price: <span class="number">5</span>,</span><br><span class="line">        quantity: <span class="number">3</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    computed: &#123;</span><br><span class="line">      total() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.price * <span class="keyword">this</span>.quantity</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&lt;script/&gt;</span><br></pre></td></tr></table></figure>
<p>当<code>price</code>发生变化后，<code>vue</code>会更新<code>price</code>的值，重新计算<code>total</code>的值并更新页面。<br>通常的js代码实现不了这样的功能<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> price = <span class="number">5</span></span><br><span class="line"><span class="keyword">let</span> quantity = <span class="number">3</span></span><br><span class="line"><span class="keyword">let</span> total = price * quantity</span><br><span class="line"><span class="built_in">console</span>.log(total) <span class="comment">// 15</span></span><br><span class="line">price = <span class="number">20</span></span><br><span class="line"><span class="built_in">console</span>.log(total) <span class="comment">// 15</span></span><br></pre></td></tr></table></figure></p>
<h4 id="如何实现响应式编程"><a href="#如何实现响应式编程" class="headerlink" title="如何实现响应式编程"></a>如何实现响应式编程</h4><h5 id="第一步：保存计算total的方法，当price或quantity变化是再次调用"><a href="#第一步：保存计算total的方法，当price或quantity变化是再次调用" class="headerlink" title="第一步：保存计算total的方法，当price或quantity变化是再次调用"></a>第一步：保存计算<code>total</code>的方法，当<code>price</code>或<code>quantity</code>变化是再次调用</h5><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> price = <span class="number">5</span></span><br><span class="line"><span class="keyword">let</span> quantity = <span class="number">3</span></span><br><span class="line"><span class="keyword">let</span> total = <span class="number">0</span></span><br><span class="line"><span class="comment">// 定义存储对象数组，用来存放需要再次调用的方法</span></span><br><span class="line"><span class="keyword">let</span> storage = []</span><br><span class="line"><span class="comment">// 定义计算total的函数名为target</span></span><br><span class="line"><span class="keyword">let</span> target = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  total = price * quantity</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 记录， 把需要再次调用的方法target放入存储对象数组</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">record</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  storage.push(target)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 定义 运行储对象数组replay</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">replay</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  storage.forEach(<span class="function"><span class="params">run</span> =&gt;</span> run())</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 调用记录函数record</span></span><br><span class="line">record()</span><br><span class="line">target()</span><br><span class="line"><span class="built_in">console</span>.log(total) <span class="comment">// 15</span></span><br><span class="line"></span><br><span class="line">price = <span class="number">20</span></span><br><span class="line">replay()</span><br><span class="line"><span class="built_in">console</span>.log(total) <span class="comment">// 60</span></span><br></pre></td></tr></table></figure>
<p>这段代码中，首先定义存储对象数组<code>storage</code>，用来存放需要再次调用的方法<code>target</code>，然后定义<code>record</code>函数用来保存我们记录的内容，然后定义了一个<code>replay</code>函数，执行<code>storage</code>数组中的每一个函数.我们下一步可以把这个封装成一个类来实现继承。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Dep</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>() &#123;</span><br><span class="line">      <span class="keyword">this</span>.subscribers = [] <span class="comment">// 相当于storage</span></span><br><span class="line">  &#125;</span><br><span class="line">  depend() &#123; <span class="comment">// 相当于record</span></span><br><span class="line">  <span class="keyword">if</span> (target &amp;&amp; !<span class="keyword">this</span>.subscribers.includes(target)) &#123;</span><br><span class="line">      <span class="keyword">this</span>.subscribers.push(target)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  notify() &#123; <span class="comment">// 相当于repaly</span></span><br><span class="line">    <span class="keyword">this</span>.subscribers.forEach(<span class="function"><span class="params">sub</span> =&gt;</span> sub())</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这个<code>Dep</code>类定义<code>subscribers</code>（订阅者）,<code>depend()</code>（订阅）方法将<code>target</code>存放到<code>subscribers</code>中，<code>notify()</code>（通知）来实现通知（执行<code>subscribers</code>数组中的每一个函数）。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Dep</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>() &#123;</span><br><span class="line">      <span class="keyword">this</span>.subscribers = [] <span class="comment">// 相当于storage</span></span><br><span class="line">  &#125;</span><br><span class="line">  depend() &#123; <span class="comment">// 相当于record</span></span><br><span class="line">  <span class="keyword">if</span> (target &amp;&amp; !<span class="keyword">this</span>.subscribers.includes(target)) &#123;</span><br><span class="line">      <span class="keyword">this</span>.subscribers.push(target)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  notify() &#123; <span class="comment">// 相当于repaly</span></span><br><span class="line">    <span class="keyword">this</span>.subscribers.forEach(<span class="function"><span class="params">sub</span> =&gt;</span> sub())</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> price = <span class="number">5</span></span><br><span class="line"><span class="keyword">let</span> quantity = <span class="number">3</span></span><br><span class="line"><span class="keyword">let</span> total = <span class="number">0</span></span><br><span class="line"><span class="keyword">const</span> dep = <span class="keyword">new</span> Dep()</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> target = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  total = price * quantity</span><br><span class="line">&#125;</span><br><span class="line">target()</span><br><span class="line">dep.depend()</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(total) <span class="comment">// 15</span></span><br><span class="line">price = <span class="number">20</span></span><br><span class="line">dep.notify()</span><br><span class="line"><span class="built_in">console</span>.log(total) <span class="comment">// 60</span></span><br></pre></td></tr></table></figure>
<p>上面的代码在通过实现Dep类的继承来实现响应式，但是在调用Dep类方法的时候不够优雅，我们继续封装一个观察者函数<code>watcher</code><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">watcher</span>(<span class="params">func</span>) </span>&#123;</span><br><span class="line">  target = func</span><br><span class="line">  dep.depend()</span><br><span class="line">  target()</span><br><span class="line">  target = <span class="literal">null</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>这个函数接受一个函数<code>func</code>，将其赋值给变量<code>target</code>，然后调用<code>dep.depend()</code>将其添加到<code>subscribers</code>中，然后调用这个函数并重置。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Dep</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>() &#123;</span><br><span class="line">      <span class="keyword">this</span>.subscribers = [] <span class="comment">// 相当于storage</span></span><br><span class="line">  &#125;</span><br><span class="line">  depend() &#123; <span class="comment">// 相当于record</span></span><br><span class="line">  <span class="keyword">if</span> (target &amp;&amp; !<span class="keyword">this</span>.subscribers.includes(target)) &#123;</span><br><span class="line">      <span class="keyword">this</span>.subscribers.push(target)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  notify() &#123; <span class="comment">// 相当于repaly</span></span><br><span class="line">    <span class="keyword">this</span>.subscribers.forEach(<span class="function"><span class="params">sub</span> =&gt;</span> sub())</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">watcher</span>(<span class="params">func</span>) </span>&#123;</span><br><span class="line">  target = func</span><br><span class="line">  dep.depend()</span><br><span class="line">  target()</span><br><span class="line">  target = <span class="literal">null</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> price = <span class="number">5</span></span><br><span class="line"><span class="keyword">let</span> quantity = <span class="number">3</span></span><br><span class="line"><span class="keyword">let</span> total = <span class="number">0</span></span><br><span class="line"><span class="keyword">const</span> dep = <span class="keyword">new</span> Dep()</span><br><span class="line"></span><br><span class="line">watcher(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  total = price * quantity</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(total) <span class="comment">// 15</span></span><br><span class="line">price = <span class="number">20</span></span><br><span class="line">dep.notify()</span><br><span class="line"><span class="built_in">console</span>.log(total) <span class="comment">// 60</span></span><br></pre></td></tr></table></figure></p>
<p>js中有一个函数是<code>Object.defineProperty()</code>,它可以为属性定义<code>getter</code>和<code>setter</code>函数，这个函数是Vue实现双向绑定的核心。<br>我们来通过<code>Object.defineProperty()</code>把data对象里的所有值重写<code>getter</code>和<code>setter</code>函数，这样只要我们的data对象里的值有变化，我们就能通过重写的<code>getter</code>和<code>setter</code>函数来实现数据的响应。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 为对象data里的所有属性重写getter和setter函数</span></span><br><span class="line"> <span class="built_in">Object</span>.keys(data).forEach(<span class="function"><span class="params">key</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> intPrice = data[key]</span><br><span class="line">    <span class="comment">// 实例化 Dep依赖类</span></span><br><span class="line">    <span class="keyword">const</span> dep = <span class="keyword">new</span> Dep()</span><br><span class="line">    <span class="built_in">Object</span>.defineProperty(data, key, &#123;</span><br><span class="line">      get() &#123;</span><br><span class="line">        <span class="comment">// 在这里实现依赖收集，把watcher观察者函数传入</span></span><br><span class="line">        dep.depend()</span><br><span class="line">        <span class="keyword">return</span> intPrice</span><br><span class="line">      &#125;,</span><br><span class="line">      set(newval) &#123;</span><br><span class="line">        <span class="keyword">if</span> (intPrice === newval) &#123;</span><br><span class="line">          <span class="keyword">return</span></span><br><span class="line">        &#125;</span><br><span class="line">        intPrice = newval</span><br><span class="line">        <span class="comment">// 在这里实现发布通知，当依赖的数据变化时，执行`subscribers`数组中的每一个函数</span></span><br><span class="line">        dep.notify()</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br></pre></td></tr></table></figure></p>
<p>完整代码如下<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> data = &#123;</span><br><span class="line">  price: <span class="number">5</span>,</span><br><span class="line">  quantity: <span class="number">3</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> target = <span class="literal">null</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义依赖类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Dep</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>() &#123;</span><br><span class="line">      <span class="keyword">this</span>.subscribers = [] <span class="comment">// 相当于storage</span></span><br><span class="line">  &#125;</span><br><span class="line">  depend() &#123; <span class="comment">// 相当于record</span></span><br><span class="line">  <span class="keyword">if</span> (target &amp;&amp; !<span class="keyword">this</span>.subscribers.includes(target)) &#123;</span><br><span class="line">      <span class="keyword">this</span>.subscribers.push(target)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  notify() &#123; <span class="comment">// 相当于repaly</span></span><br><span class="line">    <span class="keyword">this</span>.subscribers.forEach(<span class="function"><span class="params">sub</span> =&gt;</span> sub())</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 为对象data里的所有属性重写getter和setter函数</span></span><br><span class="line"><span class="built_in">Object</span>.keys(data).forEach(<span class="function"><span class="params">key</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> intPrice = data[key]</span><br><span class="line">  <span class="comment">// 实例化 Dep依赖类</span></span><br><span class="line">  <span class="keyword">const</span> dep = <span class="keyword">new</span> Dep()</span><br><span class="line">  <span class="built_in">Object</span>.defineProperty(data, key, &#123;</span><br><span class="line">    get() &#123;</span><br><span class="line">      <span class="comment">// 在这里实现依赖收集，把watcher观察者函数传入</span></span><br><span class="line">      dep.depend()</span><br><span class="line">      <span class="keyword">return</span> intPrice</span><br><span class="line">    &#125;,</span><br><span class="line">    set(newval) &#123;</span><br><span class="line">      <span class="keyword">if</span> (intPrice === newval) &#123;</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">      &#125;</span><br><span class="line">      intPrice = newval</span><br><span class="line">      <span class="comment">// 在这里实现发布通知，当依赖的数据变化时，执行`subscribers`数组中的每一个函数</span></span><br><span class="line">      dep.notify()</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 观察者函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">watcher</span>(<span class="params">func</span>) </span>&#123;</span><br><span class="line">  target = func</span><br><span class="line">  target()</span><br><span class="line">  target = <span class="literal">null</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">watcher(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  data.total = data.price * data.quantity</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></p>
<p>我们在控制台中试一试:<br><img src="/images/reactive-log.png" alt="img"></p>
<h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><ul>
<li>通过定义依赖类Dep来收集依赖项（depend）并重新运行依赖项（notify）。</li>
<li>通过定义观察者watcher来监听我们正在运行的代码，有时需要保持这些代码（target），并为其添加依赖项。</li>
<li>通过<code>Object.defineProperty()</code>,创建<code>getter</code>和<code>setter</code>函数，在<code>getter</code>中收集依赖项，当调用<code>setter</code>时通知监视器来重新执行这些依赖项</li>
</ul>

  </section>

  <section class="post-comments">

   <!--PC和WAP自适应版-->
<div id="SOHUCS" sid="请将此处替换为配置SourceID的语句" ></div> 
<script type="text/javascript"> 
(function(){ 
var appid = 'cytl2q04G'; 
var conf = 'prod_c6e77c75d2d767714e129c11e500044d'; 
var width = window.innerWidth || document.documentElement.clientWidth; 
if (width < 960) { 
window.document.write('<script id="changyan_mobile_js" charset="utf-8" type="text/javascript" src="https://changyan.sohu.com/upload/mobile/wap-js/changyan_mobile.js?client_id=' + appid + '&conf=' + conf + '"><\/script>'); } else { var loadJs=function(d,a){var c=document.getElementsByTagName("head")[0]||document.head||document.documentElement;var b=document.createElement("script");b.setAttribute("type","text/javascript");b.setAttribute("charset","UTF-8");b.setAttribute("src",d);if(typeof a==="function"){if(window.attachEvent){b.onreadystatechange=function(){var e=b.readyState;if(e==="loaded"||e==="complete"){b.onreadystatechange=null;a()}}}else{b.onload=a}}c.appendChild(b)};loadJs("https://changyan.sohu.com/upload/changyan.js",function(){window.changyan.api.config({appid:appid,conf:conf})}); } })(); </script>
    
</section>


</article>


            <footer class="footer">

    <span class="footer__copyright">&copy; 2017 | 由<a href="https://hexo.io/">Hexo</a>| 主题<a href="https://github.com/someus/huno">Huno</a></span>
    
</footer>
        </div>
    </div>

    <!-- js files -->
    <script src="/js/jquery.min.js"></script>
    <script src="/js/main.js"></script>
    <script src="/js/scale.fix.js"></script>
    

    

    <script type="text/javascript" src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
    <script type="text/javascript"> 
        $(document).ready(function(){
            MathJax.Hub.Config({ 
                tex2jax: {inlineMath: [['[latex]','[/latex]'], ['\\(','\\)']]} 
            });
        });
    </script>


    

    <script src="/js/awesome-toc.min.js"></script>
    <script>
        $(document).ready(function(){
            $.awesome_toc({
                overlay: true,
                contentId: "post-content",
            });
        });
    </script>


    
    
    <!--kill ie6 -->
<!--[if IE 6]>
  <script src="//letskillie6.googlecode.com/svn/trunk/2/zh_CN.js"></script>
<![endif]-->

</body>
</html>
